const crypto = require('crypto')
const axios = require('axios')
const smsConfig = require('../config/sms')
const redisClient = require('../config/redis')
const logger = require('../utils/logger')

class SmsService {
  constructor() {
    this.config = smsConfig
    this.provider = this.config.provider
  }

  /**
   * 发送短信验证码
   * @param {string} phone - 手机号
   * @param {string} code - 验证码
   * @returns {Promise<boolean>} 发送结果
   */
  async sendVerificationCode(phone, code) {
    try {
      // 开发环境模拟发送
      if (process.env.NODE_ENV === 'development' && this.config.development.mockSend) {
        if (this.config.development.logCode) {
          logger.info(`[开发环境] 短信验证码: ${phone} -> ${code}`)
        }
        return true
      }

      // 根据配置的服务提供商发送短信
      switch (this.provider) {
        case 'aliyun':
          return await this.sendByAliyun(phone, code)
        case 'tencent':
          return await this.sendByTencent(phone, code)
        default:
          throw new Error(`不支持的短信服务提供商: ${this.provider}`)
      }
    } catch (error) {
      logger.error('发送短信失败:', error)
      throw error
    }
  }

  /**
   * 使用阿里云发送短信
   * @param {string} phone - 手机号
   * @param {string} code - 验证码
   * @returns {Promise<boolean>} 发送结果
   */
  async sendByAliyun(phone, code) {
    try {
      const config = this.config.aliyun
      
      if (!config.accessKeyId || !config.accessKeySecret) {
        throw new Error('阿里云短信服务配置不完整')
      }

      // 构建请求参数
      const params = {
        Action: 'SendSms',
        Version: '2017-05-25',
        RegionId: 'cn-hangzhou',
        PhoneNumbers: phone,
        SignName: config.signName,
        TemplateCode: config.templateCode,
        TemplateParam: JSON.stringify({ code }),
        Format: 'JSON',
        Timestamp: new Date().toISOString(),
        SignatureMethod: 'HMAC-SHA1',
        SignatureVersion: '1.0',
        SignatureNonce: this.generateNonce(),
        AccessKeyId: config.accessKeyId
      }

      // 生成签名
      params.Signature = this.generateAliyunSignature(params, config.accessKeySecret)

      // 发送请求
      const response = await axios.post(config.endpoint, null, {
        params,
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded'
        }
      })

      if (response.data.Code === 'OK') {
        logger.info(`阿里云短信发送成功: ${phone}`)
        return true
      } else {
        throw new Error(`阿里云短信发送失败: ${response.data.Message}`)
      }
    } catch (error) {
      logger.error('阿里云短信发送失败:', error)
      throw error
    }
  }

  /**
   * 使用腾讯云发送短信
   * @param {string} phone - 手机号
   * @param {string} code - 验证码
   * @returns {Promise<boolean>} 发送结果
   */
  async sendByTencent(phone, code) {
    try {
      const config = this.config.tencent
      
      if (!config.secretId || !config.secretKey) {
        throw new Error('腾讯云短信服务配置不完整')
      }

      // 构建请求体
      const payload = {
        PhoneNumberSet: [`+86${phone}`],
        SmsSdkAppId: config.sdkAppId,
        SignName: config.signName,
        TemplateId: config.templateId,
        TemplateParamSet: [code]
      }

      // 生成认证信息
      const timestamp = Math.floor(Date.now() / 1000)
      const authorization = this.generateTencentAuth(payload, timestamp, config)

      // 发送请求
      const response = await axios.post(
        `https://sms.tencentcloudapi.com/`,
        payload,
        {
          headers: {
            'Authorization': authorization,
            'Content-Type': 'application/json; charset=utf-8',
            'Host': 'sms.tencentcloudapi.com',
            'X-TC-Action': 'SendSms',
            'X-TC-Timestamp': timestamp,
            'X-TC-Version': '2021-01-11',
            'X-TC-Region': config.region
          }
        }
      )

      if (response.data.Response && !response.data.Response.Error) {
        logger.info(`腾讯云短信发送成功: ${phone}`)
        return true
      } else {
        const error = response.data.Response?.Error
        throw new Error(`腾讯云短信发送失败: ${error?.Message || '未知错误'}`)
      }
    } catch (error) {
      logger.error('腾讯云短信发送失败:', error)
      throw error
    }
  }

  /**
   * 生成随机字符串
   * @returns {string} 随机字符串
   */
  generateNonce() {
    return Math.random().toString(36).substring(2, 15) + 
           Math.random().toString(36).substring(2, 15)
  }

  /**
   * 生成阿里云签名
   * @param {Object} params - 请求参数
   * @param {string} accessKeySecret - 密钥
   * @returns {string} 签名
   */
  generateAliyunSignature(params, accessKeySecret) {
    // 排序参数
    const sortedParams = Object.keys(params)
      .sort()
      .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
      .join('&')

    // 构建签名字符串
    const stringToSign = `POST&${encodeURIComponent('/')}&${encodeURIComponent(sortedParams)}`

    // 生成签名
    const signature = crypto
      .createHmac('sha1', `${accessKeySecret}&`)
      .update(stringToSign)
      .digest('base64')

    return signature
  }

  /**
   * 生成腾讯云认证信息
   * @param {Object} payload - 请求体
   * @param {number} timestamp - 时间戳
   * @param {Object} config - 配置
   * @returns {string} 认证信息
   */
  generateTencentAuth(payload, timestamp, config) {
    const service = 'sms'
    const host = 'sms.tencentcloudapi.com'
    const algorithm = 'TC3-HMAC-SHA256'
    const date = new Date(timestamp * 1000).toISOString().substring(0, 10)

    // 步骤1：拼接规范请求串
    const httpRequestMethod = 'POST'
    const canonicalUri = '/'
    const canonicalQueryString = ''
    const canonicalHeaders = `content-type:application/json; charset=utf-8\nhost:${host}\n`
    const signedHeaders = 'content-type;host'
    const hashedRequestPayload = crypto
      .createHash('sha256')
      .update(JSON.stringify(payload))
      .digest('hex')

    const canonicalRequest = [
      httpRequestMethod,
      canonicalUri,
      canonicalQueryString,
      canonicalHeaders,
      signedHeaders,
      hashedRequestPayload
    ].join('\n')

    // 步骤2：拼接待签名字符串
    const credentialScope = `${date}/${service}/tc3_request`
    const hashedCanonicalRequest = crypto
      .createHash('sha256')
      .update(canonicalRequest)
      .digest('hex')

    const stringToSign = [
      algorithm,
      timestamp,
      credentialScope,
      hashedCanonicalRequest
    ].join('\n')

    // 步骤3：计算签名
    const secretDate = crypto
      .createHmac('sha256', `TC3${config.secretKey}`)
      .update(date)
      .digest()
    const secretService = crypto
      .createHmac('sha256', secretDate)
      .update(service)
      .digest()
    const secretSigning = crypto
      .createHmac('sha256', secretService)
      .update('tc3_request')
      .digest()
    const signature = crypto
      .createHmac('sha256', secretSigning)
      .update(stringToSign)
      .digest('hex')

    // 步骤4：拼接Authorization
    const authorization = `${algorithm} Credential=${config.secretId}/${credentialScope}, SignedHeaders=${signedHeaders}, Signature=${signature}`

    return authorization
  }

  /**
   * 验证手机号格式
   * @param {string} phone - 手机号
   * @returns {boolean} 验证结果
   */
  validatePhone(phone) {
    const phoneRegex = /^1[3-9]\d{9}$/
    return phoneRegex.test(phone)
  }

  /**
   * 生成验证码
   * @returns {string} 验证码
   */
  generateCode() {
    const length = this.config.codeLength
    let code = ''
    for (let i = 0; i < length; i++) {
      code += Math.floor(Math.random() * 10)
    }
    return code
  }

  /**
   * 检查发送频率限制
   * @param {string} phone - 手机号
   * @returns {Promise<boolean>} 是否可以发送
   */
  async checkSendLimit(phone) {
    try {
      // 检查发送间隔
      const lastSendKey = `sms_last_send:${phone}`
      const lastSendTime = await redisClient.get(lastSendKey)
      
      if (lastSendTime) {
        const timeDiff = Date.now() - parseInt(lastSendTime)
        if (timeDiff < this.config.sendInterval * 1000) {
          const remainingTime = Math.ceil((this.config.sendInterval * 1000 - timeDiff) / 1000)
          throw new Error(`发送过于频繁，请${remainingTime}秒后再试`)
        }
      }

      // 检查每日发送次数
      const dailyCountKey = `sms_daily_count:${phone}:${new Date().toDateString()}`
      const dailyCount = await redisClient.get(dailyCountKey) || 0
      
      if (parseInt(dailyCount) >= this.config.dailyLimit) {
        throw new Error('今日发送次数已达上限')
      }

      return true
    } catch (error) {
      throw error
    }
  }

  /**
   * 记录发送信息
   * @param {string} phone - 手机号
   * @returns {Promise<void>}
   */
  async recordSendInfo(phone) {
    try {
      // 记录发送时间
      const lastSendKey = `sms_last_send:${phone}`
      await redisClient.set(lastSendKey, Date.now().toString(), { EX: this.config.sendInterval })

      // 增加每日发送次数
      const dailyCountKey = `sms_daily_count:${phone}:${new Date().toDateString()}`
      await redisClient.incr(dailyCountKey)
      await redisClient.expire(dailyCountKey, 24 * 60 * 60)
    } catch (error) {
      logger.error('记录发送信息失败:', error)
    }
  }

  /**
   * 存储验证码
   * @param {string} phone - 手机号
   * @param {string} code - 验证码
   * @returns {Promise<void>}
   */
  async storeCode(phone, code) {
    try {
      const codeKey = `sms_code:${phone}`
      await redisClient.setEx(codeKey, this.config.codeExpiry, code)
    } catch (error) {
      logger.error('存储验证码失败:', error)
      throw error
    }
  }

  /**
   * 验证验证码
   * @param {string} phone - 手机号
   * @param {string} code - 验证码
   * @returns {Promise<boolean>} 验证结果
   */
  async verifyCode(phone, code) {
    try {
      const codeKey = `sms_code:${phone}`
      const storedCode = await redisClient.get(codeKey)
      
      if (!storedCode || storedCode !== code) {
        return false
      }

      // 验证成功后删除验证码
      await redisClient.del(codeKey)
      return true
    } catch (error) {
      logger.error('验证验证码失败:', error)
      return false
    }
  }

  /**
   * 完整的发送验证码流程
   * @param {string} phone - 手机号
   * @returns {Promise<boolean>} 发送结果
   */
  async sendCodeFlow(phone) {
    try {
      // 1. 验证手机号格式
      if (!this.validatePhone(phone)) {
        throw new Error('手机号格式不正确')
      }

      // 2. 检查发送限制
      await this.checkSendLimit(phone)

      // 3. 生成验证码
      const code = this.generateCode()

      // 4. 发送短信
      await this.sendVerificationCode(phone, code)

      // 5. 存储验证码
      await this.storeCode(phone, code)

      // 6. 记录发送信息
      await this.recordSendInfo(phone)

      logger.info(`短信验证码发送成功: ${phone}`)
      return true
    } catch (error) {
      logger.error('发送验证码流程失败:', error)
      throw error
    }
  }
}

module.exports = new SmsService()